/**
 * 
 */
package galacticthrone.map.render;

import galacticthrone.main.GTGame;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.media.opengl.GL;

import org.apache.log4j.Logger;

// TODO Javadoc
/**
 * 
 *
 * @author Jaco van der Westhuizen
 */
public class IconAtlas
{
	private static final Logger logger = Logger.getLogger(IconAtlas.class);

	private final short maxIcons;

	private final short sizeIcon;
	private final byte gridX;
	private final byte gridY;
	private final float offsetX;
	private final float offsetY;
	private final float stepX;
	private final float stepY;

	private final Map<String, Short> names = new HashMap<String, Short>();

	private short nextId = 0;
	private final int[] textures = new int[] {0};
	
	public final float[][] coords;
	
	public IconAtlas(short iconSize, short maxIcons)
	{
		this.sizeIcon = iconSize;
		this.maxIcons = maxIcons;
		this.gridX = (byte)Math.ceil(Math.sqrt(maxIcons));
		this.gridY = (byte)Math.ceil(maxIcons / (float)gridX);
		this.offsetX = 0.5f / (sizeIcon*gridX);
		this.offsetY = 0.5f / (sizeIcon*gridY);
		this.stepX = 1f / gridX;
		this.stepY = 1f / gridY;
		this.coords = new float[maxIcons][2 * 4];
	}

	public short getId(GL gl, String name)
	{
		if (nextId > maxIcons)
			throw new IndexOutOfBoundsException("IconAtlas may hold only " + maxIcons + " icons.");

		Short id = names.get(name);

		if (id == null)
		{
			id = nextId++;
			names.put(name, id);
			load(gl, id, name);
		}
		
		return id;
	}
	
	private void load(GL gl, short id, String fileName)
	{
		if (textures[0] == 0)
			prepareTexture(gl);

		// Load image file
		final File file = new File(fileName);
		final int[] rgba = new int[sizeIcon * sizeIcon];
		
		try
		{
			BufferedImage img = ImageIO.read(file);

			img.getRGB(0, 0, sizeIcon, sizeIcon, rgba, 0, sizeIcon);

			for (int i = 0; i < rgba.length; i++)
			{
				final int c = rgba[i];
				rgba[i] = ((c >> 24) & 0xff) | ((c & 0xffffff) << 8);
			}
		}
		catch (IOException x)
		{
			logger.warn("Failed to load icon '" + file + "'.", x);
			
			for (int i = 0; i < rgba.length; i++)
			{
				rgba[i] = 0xff00ffff;
			}
		}

		// Place image
		final short x = (short)(id % gridX);
		final short y = (short)(id / gridX);
		final int px = x * sizeIcon;
		final int py = y * sizeIcon;

		gl.glBindTexture(GL.GL_TEXTURE_2D, textures[0]);
		gl.glTexSubImage2D(GL.GL_TEXTURE_2D, 0, px, py, sizeIcon, sizeIcon, GL.GL_RGBA, GL.GL_UNSIGNED_INT_8_8_8_8, IntBuffer.wrap(rgba));

		final float x0 = stepX * x + offsetX;
		final float y0 = stepY * y + offsetY;
		final float x1 = stepX * (x+1) - offsetX;
		final float y1 = stepY * (y+1) - offsetY;
		
		coords[id][0] = x0;
		coords[id][1] = y0;
		coords[id][2] = x0;
		coords[id][3] = y1;
		coords[id][4] = x1;
		coords[id][5] = y1;
		coords[id][6] = x1;
		coords[id][7] = y0;
	}

	/**
     * @param gl
     */
    private void prepareTexture(GL gl)
    {
	    gl.glGenTextures(1, textures, 0);
	    gl.glBindTexture(GL.GL_TEXTURE_2D, textures[0]);
	    gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA8, gridX * sizeIcon, gridY * sizeIcon, 0, GL.GL_BGRA, GL.GL_UNSIGNED_INT_8_8_8_8, null);
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
    }
    
    public void activate(GL gl)
    {
    	if (textures[0] == 0)
    		prepareTexture(gl);
    	
    	gl.glBindTexture(GL.GL_TEXTURE_2D, textures[0]);
    }
    
    public void discard(GL gl)
    {
    	gl.glDeleteTextures(1, textures, 0);
    	textures[0] = 0;
    }

	/* (non-Javadoc)
     * @see java.lang.Object#finalize()
     */
    @Override
    protected void finalize() throws Throwable
    {
	    super.finalize();

	    if (textures[0] != 0)
	    {
	    	try
	    	{
    	    	GL gl = GTGame.get().grabGL();
    	    	discard(gl);
    	    	GTGame.get().releaseGL();
	    	}
	    	catch (Throwable t)
	    	{
	    		logger.error("Failed to auto-discard IconAtlas.", t);
	    		throw t;
	    	}

	    	logger.warn("IconAtlas was auto-discarded.");
	    }
    }
    
    
}
